/* -----------------------------------------------------------------------------
 * PROJECT    : net.lwf
 * FILE       : ExpressionUtil.java
 *
 * HISTORY:
 * Date          Author                        Description
 * ----------------------------------------------------------------------------- 
 * Apr 5, 2009  Raghu Sarraff	 		        Created
 */
package net.lwf.handler.util;

import java.util.StringTokenizer;

import net.lwf.entity.Expression;
import net.lwf.entity.ExpressionData;
import net.lwf.handler.constant.IHandlerConstant;
import net.lwf.number.util.RandomNumberGenerator;


//Todo - 1. Javadoc; 2. Comparison
public class ExpressionUtil {
	
	
	public static ExpressionData getExpressionData() {
		ExpressionData expdata = null;
		int expressiontype = RandomNumberGenerator.generate(1, 6);
		switch(expressiontype) {
			case 1:
				expdata = getType1();
				break;
			case 2:
				expdata = getType2(true);
				break;
			case 3:
				expdata = getType2(false);
				break;
			case 4:
				expdata = getType3();
				break;
			case 5:
				expdata = getType4(true);
				break;
			case 6:
				expdata = getType4(false);
				break;
			default:	
				expdata = getType1();
		}
		return expdata;
	}
	
	
	/**
	 * Generates a simple expression. The expression generated in the following forms.
	 * 
	 * 3 x 5 = ? or 5 - 1 = ? or 8 / 4 = ? or 8 + 7 = ? or 7 + 6 + 5 = ? or 6 + 9 + 2 + 1 = ?
	 * 
	 * ? = 3 x 5 or ? = 5 - 1 or ? = 8 / 4 or ? =  8 + 7 or ? =  7 + 6 + 5 or ? =  6 + 9 + 2 + 1
	 * 
	 * @return ExpressionData
	 */	
	public static ExpressionData getType1() {
		Expression expression = generateExpression();
		
		ExpressionData expdata = null;
		
		int reverseIndicator = RandomNumberGenerator.generate(1, 2);
		if(reverseIndicator == 1) {
			expdata = new ExpressionData("?", expression.getExpression(), expression.getAnswer());
		}
		else {
			expdata = new ExpressionData(expression.getExpression(), "?", expression.getAnswer());
		}
		return expdata;
	}
	
	/**
	 * Generates a simple expression. The expression generated in the following forms.
	 * 
	 * 24 = (8 x ?) or 24 = (? x 8) or 24 = (12 + ?) or 20 = (? + 5) or 2 = (? - 5) or 2 = (5 - ?) and so on
	 * 
	 * (8 x ?) = 24 or (? x 8) = 24 or (12 + ?) = 18 or (? + 5) = 15 or (? - 5) = 13 or (5 - ?) = 2 and so on
	 * 
	 * @return ExpressionData
	 */
	public static ExpressionData getType2(boolean hidevalue) {
		
		ExpressionData expdata = null;
		Expression expression = generateExpression();
		
		int reverseIndicator = RandomNumberGenerator.generate(1, 2);
				
		if (reverseIndicator == 1) {
			if(hidevalue) {
				
				String[] question = hideAValue(expression.getExpression());
				expdata = new ExpressionData(question[0], expression.getAnswer(), question[1]);	
			}
			else {
				expdata = new ExpressionData("?", expression.getExpression(), expression.getAnswer());
			}
			
		} else {
			if(hidevalue) {
				String[] question = hideAValue(expression.getExpression());
				expdata = new ExpressionData(expression.getAnswer(), question[0], question[1]);	
			}
			else {
				expdata = new ExpressionData(expression.getExpression(), "?", expression.getAnswer());
			}
			
		}
		return expdata;
	}
	
	/**
	 * Generates a simple expression. The expression generated in the following forms.
	 * 
	 * (8 / 4) + 14 = ? or (8 + 4) + 14 = ? or (8 - 4) + 14 = ? or (8 x 4) x 14 = ? and so on.
	 * 
	 * Reverse order of the above
	 * 
	 * @return ExpressionData
	 */	
	public static ExpressionData getType3() {
		//ExpressionData data = null;
		ExpressionData expdata = null;
		String operation = MathUtil.getOperation();
		try {
			Expression expression = generateExpression();
			String expr = expression.getExpression();
			int expanswer = Integer.parseInt(expression.getAnswer());
			
			//Ignore Division
			while(operation.equals(IHandlerConstant.DIVISION)) {
				operation = MathUtil.getOperation();
			}
			int thirdArgument = 0;
			
			if(operation.equals(IHandlerConstant.ADDITION)) {
				thirdArgument = RandomNumberGenerator.generate(1, 100);
				expanswer += thirdArgument;
			} 
			else if(operation.equals(IHandlerConstant.SUBTRACTION)) {
				thirdArgument = RandomNumberGenerator.generate(1, 10);
				while(thirdArgument > expanswer) {
					thirdArgument = RandomNumberGenerator.generate(1, 10);
				}
				expanswer = expanswer - thirdArgument;				
			}
			else {
				thirdArgument = RandomNumberGenerator.generate(1, 10);
				expanswer = expanswer * thirdArgument;
			}
			
			
			int reverseIndicator = RandomNumberGenerator.generate(1, 2);
			
			if(reverseIndicator == 1) {
				//expr = expr + " " + MathUtil.getOperationsign(operation) + " " + thirdArgument + " = ?";
				expr = expr + " " + MathUtil.getOperationsign(operation) + " " + thirdArgument;
				expdata = new ExpressionData(expr, "?", String.valueOf(expanswer));
			}
			else {
				//expr = "? = " + expr + " " + MathUtil.getOperationsign(operation) + " " + thirdArgument;
				expr = expr + " " + MathUtil.getOperationsign(operation) + " " + thirdArgument;
				expdata = new ExpressionData("?", expr, String.valueOf(expanswer));
			}
			//data = new ExpressionData(expr, String.valueOf(expanswer));
			
		}
		catch(Exception ex) {
			System.out.println("==============> Failed for an operation: " + operation);
		}
		
		
		//return data;
		return expdata;
	}
	
	//(3 x 5) + (3 x 5) + 3 x 5 ................
	public static ExpressionData getType4() {
		int finalanswer = 0;
		//ExpressionData data = null;
		ExpressionData expdata = null;
		String finalexprstring = null;
		
		while (finalanswer == 0 || finalanswer < 0) {
			int noofexpressions = RandomNumberGenerator.generate(1, 4); //Key Factor

			Expression expression = generateExpression();
			finalexprstring = expression.getExpression();
			finalanswer = Integer.parseInt(expression.getAnswer());

			for (int i = 1; i < noofexpressions; i++) {
				String operation = MathUtil.getOperation();
				try {
					Expression currexpression = generateExpression();
					String currexprstring = currexpression.getExpression();
					int curranswer = Integer.parseInt(currexpression.getAnswer());
					//Ignore Division
					while (operation.equals(IHandlerConstant.DIVISION)) {
						operation = MathUtil.getOperation();
					}

					if (operation.equals(IHandlerConstant.ADDITION)) {
						finalanswer += curranswer;
					} else if (operation.equals(IHandlerConstant.SUBTRACTION)) {
						finalanswer = finalanswer - curranswer;
					} else {
						finalanswer = finalanswer * curranswer;
					}

					finalexprstring = finalexprstring + " "
							+ MathUtil.getOperationsign(operation) + " "
							+ currexprstring;

				} catch (Exception ex) {
					System.out.println("==============> Failed for an operation: " + operation);
				}
			}
		}
		
		int reverseIndicator = RandomNumberGenerator.generate(1, 2);

		if (reverseIndicator == 1) {
			expdata = new ExpressionData(finalexprstring, "?", String.valueOf(finalanswer));
			//finalexprstring = finalexprstring + " = ?";
		} else {
			//finalexprstring = "? = " + finalexprstring;
			expdata = new ExpressionData("?", finalexprstring, String.valueOf(finalanswer));
		}
		//data = new ExpressionData(finalexprstring, String.valueOf(finalanswer));

		//return data;
		return expdata;
	}
	
	/**
	 * The hidevalue attribute defines to have a question mark in the expression or not.
	 *  
	 * @param hidevalue
	 * @return
	 */
	public static ExpressionData getType4(boolean hidevalue) {
		int finalanswer = 0;
		//ExpressionData data = null;
		ExpressionData expdata = null;
		String finalexprstring = null;
		String hiddenexpressionvalue[] = null;
		while (finalanswer == 0 || finalanswer < 0) {
			int noofexpressions = RandomNumberGenerator.generate(1, 4); //Key Factor
			int hiddenexpression = 0;
			if(hidevalue) {
				hiddenexpression = RandomNumberGenerator.generate(1, noofexpressions);
			}
            
			Expression expression = generateExpression();
			finalexprstring = expression.getExpression();
			finalanswer = Integer.parseInt(expression.getAnswer());
			
			if(hiddenexpression == 1) {
				hiddenexpressionvalue = hideAValue(finalexprstring);
				finalexprstring = hiddenexpressionvalue[0];
			}

			for (int i = 1; i < noofexpressions; i++) {
				String operation = MathUtil.getOperation();
				try {
					Expression currexpression = generateExpression();
					String currexprstring = currexpression.getExpression();
					int curranswer = Integer.parseInt(currexpression.getAnswer());
					if(hiddenexpression == i+1) {
						hiddenexpressionvalue = hideAValue(currexprstring);
						currexprstring = hiddenexpressionvalue[0];
					}					
					//Ignore Division
					while (operation.equals(IHandlerConstant.DIVISION)) {
						operation = MathUtil.getOperation();
					}

					if (operation.equals(IHandlerConstant.ADDITION)) {
						finalanswer += curranswer;
					} else if (operation.equals(IHandlerConstant.SUBTRACTION)) {
						finalanswer = finalanswer - curranswer;
					} else {
						finalanswer = finalanswer * curranswer;
					}

					finalexprstring = finalexprstring + " "
							+ MathUtil.getOperationsign(operation) + " "
							+ currexprstring;

				} catch (Exception ex) {
					System.out.println("==============> Failed for an operation: " + operation);
				}
			}
		}
		
		int reverseIndicator = RandomNumberGenerator.generate(1, 2);

		if (reverseIndicator == 1) {
			//finalexprstring = finalexprstring + " = " + String.valueOf(finalanswer);
			if(hidevalue) {
				expdata = new ExpressionData(finalexprstring, String.valueOf(finalanswer), hiddenexpressionvalue[1]);	
			}
			else {
				expdata = new ExpressionData(finalexprstring, "?", String.valueOf(finalanswer));
			}
			
		} else {
			//finalexprstring = String.valueOf(finalanswer) + " = " + finalexprstring;
			if(hidevalue) {
				expdata = new ExpressionData(String.valueOf(finalanswer), finalexprstring, hiddenexpressionvalue[1]);	
			}
			else {
				expdata = new ExpressionData("?", finalexprstring, String.valueOf(finalanswer));
			}
			
		}
		//data = new ExpressionData(finalexprstring, hiddenexpressionvalue[1]);

		//return data;
		return expdata;
	}
	
	//24 = (8 x ?) + () + ()
	
	
	public static ExpressionData getComparator() {
		ExpressionData expr1 = null;
		ExpressionData expr2 = null;
		int expressiontype = RandomNumberGenerator.generate(0, 9);
		switch(expressiontype) {
			case 1:
				expr1 = getType2(false);
				expr2 = getType2(false);
				break;
			case 2:
				expr1 = getType1();
				expr2 = getType2(false);
				break;
			case 3:
				expr1 = getType3();
				expr2 = getType3();
				break;
			case 4:
				expr1 = getType3();
				expr2 = getType2(false);
				break;
			case 5:
				expr1 = getType3();
				expr2 = getType1();
				break;
			case 6:
				expr1 = getType4(false);
				expr2 = getType4(false);
				break;
			case 7:
				expr1 = getType4(false);
				expr2 = getType3();
				break;
			case 8:
				expr1 = getType4(false);
				expr2 = getType2(false);
				break;
			case 9:
				expr1 = getType4(false);
				expr2 = getType1();
				break;
			default:
				//0
				expr1 = getType1();
				expr2 = getType1();
		}
		return constructComparativeExpression(expr1, expr2);
	}
	
	
	//Private methods
	
	
	
	/**
	 * @return
	 */
	private static ExpressionData constructComparativeExpression(ExpressionData expr1, ExpressionData expr2) {
		String lefthandexpr = null;
		String righthandexpr = null;
		String answer = null;
		ExpressionData edata = null;
		
		
		if(expr1.getLhsExpression().equals("?")) {
			lefthandexpr = expr1.getRhsExpression();
		}
		else {
			lefthandexpr = expr1.getLhsExpression();
		}
		
		if(expr2.getLhsExpression().equals("?")) {
			righthandexpr = expr2.getRhsExpression();
		}
		else {
			righthandexpr = expr2.getLhsExpression();
		}
		double exprAnswer1 = new Double(expr1.getAnswer()).doubleValue();
		double exprAnswer2 = new Double(expr2.getAnswer()).doubleValue();
		 
		if(exprAnswer1 < exprAnswer2) {
			answer = "<";
		}
		else if(exprAnswer1 > exprAnswer2) {
			answer = ">";
		}
		else {
			answer = "=";
		}
		edata = new ExpressionData(lefthandexpr, righthandexpr, answer);
		
		return edata;
	}

	private static Expression generateExpression() {
		Expression expression = null;
		String operation = MathUtil.getOperation();
		int requirednumerals = 2;
		int[] expnumerals = null;
		if(operation.equals(IHandlerConstant.ADDITION)) {
			requirednumerals = RandomNumberGenerator.generate(2, 4);
			expnumerals = RandomNumberGenerator.generateInts(2, 10, requirednumerals, false);
		} 
		else {
			expnumerals = RandomNumberGenerator.generateInts(2, 10, requirednumerals, false);
		}
		if(operation.equals(IHandlerConstant.DIVISION)) {
			int remainder = expnumerals[0] % expnumerals[1];
			while(remainder != 0) {
				expnumerals = RandomNumberGenerator.generateInts(2, 10, requirednumerals, false);
				remainder = expnumerals[0] % expnumerals[1];
			}
		}
		return new Expression(operation, expnumerals);
	}
	
	
	
	
	
	/**
	 * Hides a value in the expression (by a question mark).
	 * 
	 * The return element is a string array inwhich the first element is 
	 * an expression and the second element is the hidden value.
	 * For example, for an expression of (8 x 4), the first element is (8 x ?)
	 * and the second element will be 4
	 * 
	 * @param expression
	 * 
	 * @return
	 */
	private static String[] hideAValue(String expression) {
		//		Check and remove first character 
		if (expression.indexOf("(") != -1) {
			expression = expression.substring(1, expression.length() - 1);
		}

		//		Tokenize the values 
		String operation = "";
		StringTokenizer stk = null;
		if (expression.indexOf("+") != -1) {
			operation = "+";
		}
		else if (expression.indexOf("-") != -1) {
			operation = "-";
		}
		else if (expression.indexOf("x") != -1) {
			operation = "x";
		}
		else {
			operation = "/";
		}

		stk = new StringTokenizer(expression, operation);
		int nTokens = stk.countTokens();
		String[] data = new String[nTokens];
		int nCounter = 0;
		while (stk.hasMoreTokens()) {
			data[nCounter] = stk.nextToken().trim();
			nCounter++;
		}

		int replacevalueposition = RandomNumberGenerator.generate(1, nTokens);
		String hiddenvalue = data[replacevalueposition - 1];
		data[replacevalueposition - 1] = "?";

		//		Reconstruct the expression 
		String newexprnstring = "";
		for (int i = 0; i < data.length; i++) {
			newexprnstring = newexprnstring + data[i];
			if (i + 1 < data.length) {
				newexprnstring = newexprnstring + " " + operation + " ";
			}
		}
		if (!operation.equals(IHandlerConstant.ADDITION) || nTokens > 2) {
			newexprnstring = "(" + newexprnstring + ")";
		}
        String[] questionanswer = {newexprnstring, hiddenvalue};
  
		return questionanswer;
	}
}

